/*
 * Copyright 2013 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "src/sfnt/SkOTTable_name.h"

#include "src/base/SkEndian.h"
#include "src/base/SkTSearch.h"
#include "src/base/SkUTF.h"
#include "src/core/SkStringUtils.h"

static SkUnichar next_unichar_UTF16BE(const uint8_t **srcPtr, size_t *length)
{
    SkASSERT(srcPtr && *srcPtr && length);
    SkASSERT(*length > 0);

    uint16_t leading;
    if (*length < sizeof(leading)) {
        *length = 0;
        return 0xFFFD;
    }
    memcpy(&leading, *srcPtr, sizeof(leading));
    *srcPtr += sizeof(leading);
    *length -= sizeof(leading);
    SkUnichar c = SkEndian_SwapBE16(leading);

    if (SkUTF::IsTrailingSurrogateUTF16(c)) {
        return 0xFFFD;
    }
    if (SkUTF::IsLeadingSurrogateUTF16(c)) {
        uint16_t trailing;
        if (*length < sizeof(trailing)) {
            *length = 0;
            return 0xFFFD;
        }
        memcpy(&trailing, *srcPtr, sizeof(trailing));
        SkUnichar c2 = SkEndian_SwapBE16(trailing);
        if (!SkUTF::IsTrailingSurrogateUTF16(c2)) {
            return 0xFFFD;
        }
        *srcPtr += sizeof(trailing);
        *length -= sizeof(trailing);

        c = (c << 10) + c2 + (0x10000 - (0xD800 << 10) - 0xDC00);
    }
    return c;
}

static void SkString_from_UTF16BE(const uint8_t *utf16be, size_t length, SkString &utf8)
{
    // Note that utf16be may not be 2-byte aligned.
    SkASSERT(utf16be != nullptr);

    utf8.reset();
    while (length) {
        utf8.appendUnichar(next_unichar_UTF16BE(&utf16be, &length));
    }
}

/* * UnicodeFromMacRoman[macRomanPoint - 0x80] -> unicodeCodePoint.
 * Derived from http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/ROMAN.TXT .
 * In MacRoman the first 128 code points match ASCII code points.
 * This maps the second 128 MacRoman code points to unicode code points.
 */
static const uint16_t UnicodeFromMacRoman[0x80] = {
    0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1,
    0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8,
    0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3,
    0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC,
    0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF,
    0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8,
    0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211,
    0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x03A9, 0x00E6, 0x00F8,
    0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB,
    0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153,
    0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA,
    0x00FF, 0x0178, 0x2044, 0x20AC, 0x2039, 0x203A, 0xFB01, 0xFB02,
    0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1,
    0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4,
    0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC,
    0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7,
};

static void SkStringFromMacRoman(const uint8_t *macRoman, size_t length, SkString &utf8)
{
    utf8.reset();
    for (size_t i = 0; i < length; ++i) {
        utf8.appendUnichar(macRoman[i] < 0x80 ? macRoman[i] : UnicodeFromMacRoman[macRoman[i] - 0x80]);
    }
}

static const struct BCP47FromLanguageId {
    uint16_t languageID;
    const char *bcp47;
}
/* * The Mac and Windows values do not conflict, so this is currently one single table. */
BCP47FromLanguageID[] = {
    /* * A mapping from Mac Language Designators to BCP 47 codes.
     * The following list was constructed more or less manually.
     * Apple now uses BCP 47 (post OSX10.4), so there will be no new entries.
     */
    {0, "en"},        // English
    {1, "fr"},        // French
    {2, "de"},        // German
    {3, "it"},        // Italian
    {4, "nl"},        // Dutch
    {5, "sv"},        // Swedish
    {6, "es"},        // Spanish
    {7, "da"},        // Danish
    {8, "pt"},        // Portuguese
    {9, "nb"},        // Norwegian
    {10, "he"},       // Hebrew
    {11, "ja"},       // Japanese
    {12, "ar"},       // Arabic
    {13, "fi"},       // Finnish
    {14, "el"},       // Greek
    {15, "is"},       // Icelandic
    {16, "mt"},       // Maltese
    {17, "tr"},       // Turkish
    {18, "hr"},       // Croatian
    {19, "zh-Hant"},  // Chinese (Traditional)
    {20, "ur"},       // Urdu
    {21, "hi"},       // Hindi
    {22, "th"},       // Thai
    {23, "ko"},       // Korean
    {24, "lt"},       // Lithuanian
    {25, "pl"},       // Polish
    {26, "hu"},       // Hungarian
    {27, "et"},       // Estonian
    {28, "lv"},       // Latvian
    {29, "se"},       // Sami
    {30, "fo"},       // Faroese
    {31, "fa"},       // Farsi (Persian)
    {32, "ru"},       // Russian
    {33, "zh-Hans"},  // Chinese (Simplified)
    {34, "nl"},       // Dutch
    {35, "ga"},       // Irish(Gaelic)
    {36, "sq"},       // Albanian
    {37, "ro"},       // Romanian
    {38, "cs"},       // Czech
    {39, "sk"},       // Slovak
    {40, "sl"},       // Slovenian
    {41, "yi"},       // Yiddish
    {42, "sr"},       // Serbian
    {43, "mk"},       // Macedonian
    {44, "bg"},       // Bulgarian
    {45, "uk"},       // Ukrainian
    {46, "be"},       // Byelorussian
    {47, "uz"},       // Uzbek
    {48, "kk"},       // Kazakh
    {49, "az-Cyrl"},  // Azerbaijani (Cyrillic)
    {50, "az-Arab"},  // Azerbaijani (Arabic)
    {51, "hy"},       // Armenian
    {52, "ka"},       // Georgian
    {53, "mo"},       // Moldavian
    {54, "ky"},       // Kirghiz
    {55, "tg"},       // Tajiki
    {56, "tk"},       // Turkmen
    {57, "mn-Mong"},  // Mongolian (Traditional)
    {58, "mn-Cyrl"},  // Mongolian (Cyrillic)
    {59, "ps"},       // Pashto
    {60, "ku"},       // Kurdish
    {61, "ks"},       // Kashmiri
    {62, "sd"},       // Sindhi
    {63, "bo"},       // Tibetan
    {64, "ne"},       // Nepali
    {65, "sa"},       // Sanskrit
    {66, "mr"},       // Marathi
    {67, "bn"},       // Bengali
    {68, "as"},       // Assamese
    {69, "gu"},       // Gujarati
    {70, "pa"},       // Punjabi
    {71, "or"},       // Oriya
    {72, "ml"},       // Malayalam
    {73, "kn"},       // Kannada
    {74, "ta"},       // Tamil
    {75, "te"},       // Telugu
    {76, "si"},       // Sinhalese
    {77, "my"},       // Burmese
    {78, "km"},       // Khmer
    {79, "lo"},       // Lao
    {80, "vi"},       // Vietnamese
    {81, "id"},       // Indonesian
    {82, "tl"},       // Tagalog
    {83, "ms-Latn"},  // Malay (Roman)
    {84, "ms-Arab"},  // Malay (Arabic)
    {85, "am"},       // Amharic
    {86, "ti"},       // Tigrinya
    {87, "om"},       // Oromo
    {88, "so"},       // Somali
    {89, "sw"},       // Swahili
    {90, "rw"},       // Kinyarwanda/Ruanda
    {91, "rn"},       // Rundi
    {92, "ny"},       // Nyanja/Chewa
    {93, "mg"},       // Malagasy
    {94, "eo"},       // Esperanto
    {128, "cy"},      // Welsh
    {129, "eu"},      // Basque
    {130, "ca"},      // Catalan
    {131, "la"},      // Latin
    {132, "qu"},      // Quechua
    {133, "gn"},      // Guarani
    {134, "ay"},      // Aymara
    {135, "tt"},      // Tatar
    {136, "ug"},      // Uighur
    {137, "dz"},      // Dzongkha
    {138, "jv-Latn"}, // Javanese (Roman)
    {139, "su-Latn"}, // Sundanese (Roman)
    {140, "gl"},      // Galician
    {141, "af"},      // Afrikaans
    {142, "br"},      // Breton
    {143, "iu"},      // Inuktitut
    {144, "gd"},      // Scottish (Gaelic)
    {145, "gv"},      // Manx (Gaelic)
    {146, "ga"},      // Irish (Gaelic with Lenition)
    {147, "to"},      // Tongan
    {148, "el"},      // Greek (Polytonic) Note: ISO 15924 does not have an equivalent script name.
    {149, "kl"},      // Greenlandic
    {150, "az-Latn"}, // Azerbaijani (Roman)
    {151, "nn"},      // Nynorsk

    /* * A mapping from Windows LCID to BCP 47 codes.
     * This list is the sorted, curated output of tools/win_lcid.cpp.
     * Note that these are sorted by value for quick binary lookup, and not logically by lsb.
     * The 'bare' language ids (e.g. 0x0001 for Arabic) are ommitted
     * as they do not appear as valid language ids in the OpenType specification.
     */
    { 0x0401, "ar-SA" },        // Arabic
    { 0x0402, "bg-BG" },        // Bulgarian
    { 0x0403, "ca-ES" },        // Catalan
    { 0x0404, "zh-TW" },        // Chinese (Traditional)
    { 0x0405, "cs-CZ" },        // Czech
    { 0x0406, "da-DK" },        // Danish
    { 0x0407, "de-DE" },        // German
    { 0x0408, "el-GR" },        // Greek
    { 0x0409, "en-US" },        // English
    { 0x040a, "es-ES_tradnl" }, // Spanish
    { 0x040b, "fi-FI" },        // Finnish
    { 0x040c, "fr-FR" },        // French
    { 0x040d, "he-IL" },        // Hebrew
    { 0x040d, "he" },           // Hebrew
    { 0x040e, "hu-HU" },        // Hungarian
    { 0x040e, "hu" },           // Hungarian
    { 0x040f, "is-IS" },        // Icelandic
    { 0x0410, "it-IT" },        // Italian
    { 0x0411, "ja-JP" },        // Japanese
    { 0x0412, "ko-KR" },        // Korean
    { 0x0413, "nl-NL" },        // Dutch
    { 0x0414, "nb-NO" },        // Norwegian (Bokmål)
    { 0x0415, "pl-PL" },        // Polish
    { 0x0416, "pt-BR" },        // Portuguese
    { 0x0417, "rm-CH" },        // Romansh
    { 0x0418, "ro-RO" },        // Romanian
    { 0x0419, "ru-RU" },        // Russian
    { 0x041a, "hr-HR" },        // Croatian
    { 0x041b, "sk-SK" },        // Slovak
    { 0x041c, "sq-AL" },        // Albanian
    { 0x041d, "sv-SE" },        // Swedish
    { 0x041e, "th-TH" },        // Thai
    { 0x041f, "tr-TR" },        // Turkish
    { 0x0420, "ur-PK" },        // Urdu
    { 0x0421, "id-ID" },        // Indonesian
    { 0x0422, "uk-UA" },        // Ukrainian
    { 0x0423, "be-BY" },        // Belarusian
    { 0x0424, "sl-SI" },        // Slovenian
    { 0x0425, "et-EE" },        // Estonian
    { 0x0426, "lv-LV" },        // Latvian
    { 0x0427, "lt-LT" },        // Lithuanian
    { 0x0428, "tg-Cyrl-TJ" },   // Tajik (Cyrillic)
    { 0x0429, "fa-IR" },        // Persian
    { 0x042a, "vi-VN" },        // Vietnamese
    { 0x042b, "hy-AM" },        // Armenian
    { 0x042c, "az-Latn-AZ" },   // Azeri (Latin)
    { 0x042d, "eu-ES" },        // Basque
    { 0x042e, "hsb-DE" },       // Upper Sorbian
    { 0x042f, "mk-MK" },        // Macedonian (FYROM)
    { 0x0432, "tn-ZA" },        // Setswana
    { 0x0434, "xh-ZA" },        // isiXhosa
    { 0x0435, "zu-ZA" },        // isiZulu
    { 0x0436, "af-ZA" },        // Afrikaans
    { 0x0437, "ka-GE" },        // Georgian
    { 0x0438, "fo-FO" },        // Faroese
    { 0x0439, "hi-IN" },        // Hindi
    { 0x043a, "mt-MT" },        // Maltese
    { 0x043b, "se-NO" },        // Sami (Northern)
    { 0x043e, "ms-MY" },        // Malay
    { 0x043f, "kk-KZ" },        // Kazakh
    { 0x0440, "ky-KG" },        // Kyrgyz
    { 0x0441, "sw-KE" },        // Kiswahili
    { 0x0442, "tk-TM" },        // Turkmen
    { 0x0443, "uz-Latn-UZ" },   // Uzbek (Latin)
    { 0x0443, "uz" },           // Uzbek
    { 0x0444, "tt-RU" },        // Tatar
    { 0x0445, "bn-IN" },        // Bengali
    { 0x0446, "pa-IN" },        // Punjabi
    { 0x0447, "gu-IN" },        // Gujarati
    { 0x0448, "or-IN" },        // Oriya
    { 0x0449, "ta-IN" },        // Tamil
    { 0x044a, "te-IN" },        // Telugu
    { 0x044b, "kn-IN" },        // Kannada
    { 0x044c, "ml-IN" },        // Malayalam
    { 0x044d, "as-IN" },        // Assamese
    { 0x044e, "mr-IN" },        // Marathi
    { 0x044f, "sa-IN" },        // Sanskrit
    { 0x0450, "mn-Cyrl" },      // Mongolian (Cyrillic)
    { 0x0451, "bo-CN" },        // Tibetan
    { 0x0452, "cy-GB" },        // Welsh
    { 0x0453, "km-KH" },        // Khmer
    { 0x0454, "lo-LA" },        // Lao
    { 0x0456, "gl-ES" },        // Galician
    { 0x0457, "kok-IN" },       // Konkani
    { 0x045a, "syr-SY" },       // Syriac
    { 0x045b, "si-LK" },        // Sinhala
    { 0x045d, "iu-Cans-CA" },   // Inuktitut (Syllabics)
    { 0x045e, "am-ET" },        // Amharic
    { 0x0461, "ne-NP" },        // Nepali
    { 0x0462, "fy-NL" },        // Frisian
    { 0x0463, "ps-AF" },        // Pashto
    { 0x0464, "fil-PH" },       // Filipino
    { 0x0465, "dv-MV" },        // Divehi
    { 0x0468, "ha-Latn-NG" },   // Hausa (Latin)
    { 0x046a, "yo-NG" },        // Yoruba
    { 0x046b, "quz-BO" },       // Quechua
    { 0x046c, "nso-ZA" },       // Sesotho sa Leboa
    { 0x046d, "ba-RU" },        // Bashkir
    { 0x046e, "lb-LU" },        // Luxembourgish
    { 0x046f, "kl-GL" },        // Greenlandic
    { 0x0470, "ig-NG" },        // Igbo
    { 0x0478, "ii-CN" },        // Yi
    { 0x047a, "arn-CL" },       // Mapudungun
    { 0x047c, "moh-CA" },       // Mohawk
    { 0x047e, "br-FR" },        // Breton
    { 0x0480, "ug-CN" },        // Uyghur
    { 0x0481, "mi-NZ" },        // Maori
    { 0x0482, "oc-FR" },        // Occitan
    { 0x0483, "co-FR" },        // Corsican
    { 0x0484, "gsw-FR" },       // Alsatian
    { 0x0485, "sah-RU" },       // Yakut
    { 0x0486, "qut-GT" },       // K'iche
    { 0x0487, "rw-RW" },        // Kinyarwanda
    { 0x0488, "wo-SN" },        // Wolof
    { 0x048c, "prs-AF" },       // Dari
    { 0x0491, "gd-GB" },        // Scottish Gaelic
    { 0x0801, "ar-IQ" },        // Arabic
    { 0x0804, "zh-Hans" },      // Chinese (Simplified)
    { 0x0807, "de-CH" },        // German
    { 0x0809, "en-GB" },        // English
    { 0x080a, "es-MX" },        // Spanish
    { 0x080c, "fr-BE" },        // French
    { 0x0810, "it-CH" },        // Italian
    { 0x0813, "nl-BE" },        // Dutch
    { 0x0814, "nn-NO" },        // Norwegian (Nynorsk)
    { 0x0816, "pt-PT" },        // Portuguese
    { 0x081a, "sr-Latn-CS" },   // Serbian (Latin)
    { 0x081d, "sv-FI" },        // Swedish
    { 0x082c, "az-Cyrl-AZ" },   // Azeri (Cyrillic)
    { 0x082e, "dsb-DE" },       // Lower Sorbian
    { 0x082e, "dsb" },          // Lower Sorbian
    { 0x083b, "se-SE" },        // Sami (Northern)
    { 0x083c, "ga-IE" },        // Irish
    { 0x083e, "ms-BN" },        // Malay
    { 0x0843, "uz-Cyrl-UZ" },   // Uzbek (Cyrillic)
    { 0x0845, "bn-BD" },        // Bengali
    { 0x0850, "mn-Mong-CN" },   // Mongolian (Traditional Mongolian)
    { 0x085d, "iu-Latn-CA" },   // Inuktitut (Latin)
    { 0x085f, "tzm-Latn-DZ" },  // Tamazight (Latin)
    { 0x086b, "quz-EC" },       // Quechua
    { 0x0c01, "ar-EG" },        // Arabic
    { 0x0c04, "zh-Hant" },      // Chinese (Traditional)
    { 0x0c07, "de-AT" },        // German
    { 0x0c09, "en-AU" },        // English
    { 0x0c0a, "es-ES" },        // Spanish
    { 0x0c0c, "fr-CA" },        // French
    { 0x0c1a, "sr-Cyrl-CS" },   // Serbian (Cyrillic)
    { 0x0c3b, "se-FI" },        // Sami (Northern)
    { 0x0c6b, "quz-PE" },       // Quechua
    { 0x1001, "ar-LY" },        // Arabic
    { 0x1004, "zh-SG" },        // Chinese (Simplified)
    { 0x1007, "de-LU" },        // German
    { 0x1009, "en-CA" },        // English
    { 0x100a, "es-GT" },        // Spanish
    { 0x100c, "fr-CH" },        // French
    { 0x101a, "hr-BA" },        // Croatian (Latin)
    { 0x103b, "smj-NO" },       // Sami (Lule)
    { 0x1401, "ar-DZ" },        // Arabic
    { 0x1404, "zh-MO" },        // Chinese (Traditional)
    { 0x1407, "de-LI" },        // German
    { 0x1409, "en-NZ" },        // English
    { 0x140a, "es-CR" },        // Spanish
    { 0x140c, "fr-LU" },        // French
    { 0x141a, "bs-Latn-BA" },   // Bosnian (Latin)
    { 0x141a, "bs" },           // Bosnian
    { 0x143b, "smj-SE" },       // Sami (Lule)
    { 0x143b, "smj" },          // Sami (Lule)
    { 0x1801, "ar-MA" },        // Arabic
    { 0x1809, "en-IE" },        // English
    { 0x180a, "es-PA" },        // Spanish
    { 0x180c, "fr-MC" },        // French
    { 0x181a, "sr-Latn-BA" },   // Serbian (Latin)
    { 0x183b, "sma-NO" },       // Sami (Southern)
    { 0x1c01, "ar-TN" },        // Arabic
    { 0x1c09, "en-ZA" },        // English
    { 0x1c0a, "es-DO" },        // Spanish
    { 0x1c1a, "sr-Cyrl-BA" },   // Serbian (Cyrillic)
    { 0x1c3b, "sma-SE" },       // Sami (Southern)
    { 0x1c3b, "sma" },          // Sami (Southern)
    { 0x2001, "ar-OM" },        // Arabic
    { 0x2009, "en-JM" },        // English
    { 0x200a, "es-VE" },        // Spanish
    { 0x201a, "bs-Cyrl-BA" },   // Bosnian (Cyrillic)
    { 0x201a, "bs-Cyrl" },      // Bosnian (Cyrillic)
    { 0x203b, "sms-FI" },       // Sami (Skolt)
    { 0x203b, "sms" },          // Sami (Skolt)
    { 0x2401, "ar-YE" },        // Arabic
    { 0x2409, "en-029" },       // English
    { 0x240a, "es-CO" },        // Spanish
    { 0x241a, "sr-Latn-RS" },   // Serbian (Latin)
    { 0x243b, "smn-FI" },       // Sami (Inari)
    { 0x2801, "ar-SY" },        // Arabic
    { 0x2809, "en-BZ" },        // English
    { 0x280a, "es-PE" },        // Spanish
    { 0x281a, "sr-Cyrl-RS" },   // Serbian (Cyrillic)
    { 0x2c01, "ar-JO" },        // Arabic
    { 0x2c09, "en-TT" },        // English
    { 0x2c0a, "es-AR" },        // Spanish
    { 0x2c1a, "sr-Latn-ME" },   // Serbian (Latin)
    { 0x3001, "ar-LB" },        // Arabic
    { 0x3009, "en-ZW" },        // English
    { 0x300a, "es-EC" },        // Spanish
    { 0x301a, "sr-Cyrl-ME" },   // Serbian (Cyrillic)
    { 0x3401, "ar-KW" },        // Arabic
    { 0x3409, "en-PH" },        // English
    { 0x340a, "es-CL" },        // Spanish
    { 0x3801, "ar-AE" },        // Arabic
    { 0x380a, "es-UY" },        // Spanish
    { 0x3c01, "ar-BH" },        // Arabic
    { 0x3c0a, "es-PY" },        // Spanish
    { 0x4001, "ar-QA" },        // Arabic
    { 0x4009, "en-IN" },        // English
    { 0x400a, "es-BO" },        // Spanish
    { 0x4409, "en-MY" },        // English
    { 0x440a, "es-SV" },        // Spanish
    { 0x4809, "en-SG" },        // English
    { 0x480a, "es-HN" },        // Spanish
    { 0x4c0a, "es-NI" },        // Spanish
    { 0x500a, "es-PR" },        // Spanish
    { 0x540a, "es-US" },        // Spanish
};

namespace {
bool BCP47FromLanguageIdLess(const BCP47FromLanguageId &a, const BCP47FromLanguageId &b)
{
    return a.languageID < b.languageID;
}
} // namespace

bool SkOTTableName::Iterator::next(SkOTTableName::Iterator::Record &record)
{
    SkOTTableName nameTable;
    if (fNameTableSize < sizeof(nameTable)) {
        return false;
    }
    memcpy(&nameTable, fNameTable, sizeof(nameTable));

    const uint8_t *nameRecords = fNameTable + sizeof(nameTable);
    const size_t nameRecordsSize = fNameTableSize - sizeof(nameTable);

    const size_t stringTableOffset = SkEndian_SwapBE16(nameTable.stringOffset);
    if (fNameTableSize < stringTableOffset) {
        return false;
    }
    const uint8_t *stringTable = fNameTable + stringTableOffset;
    const size_t stringTableSize = fNameTableSize - stringTableOffset;

    // Find the next record which matches the requested type.
    SkOTTableName::Record nameRecord;
    const size_t nameRecordsCount = SkEndian_SwapBE16(nameTable.count);
    const size_t nameRecordsMax = std::min(nameRecordsCount, nameRecordsSize / sizeof(nameRecord));
    do {
        if (fIndex >= nameRecordsMax) {
            return false;
        }

        memcpy(&nameRecord, nameRecords + sizeof(nameRecord) * fIndex, sizeof(nameRecord));
        ++fIndex;
    } while (fType != -1 && nameRecord.nameID.fontSpecific != fType);

    record.type = nameRecord.nameID.fontSpecific;

    // Decode the name into UTF-8.
    const size_t nameOffset = SkEndian_SwapBE16(nameRecord.offset);
    const size_t nameLength = SkEndian_SwapBE16(nameRecord.length);
    if (stringTableSize < nameOffset + nameLength) {
        return false; // continue?
    }
    const uint8_t *nameString = stringTable + nameOffset;
    switch (nameRecord.platformID.value) {
        case SkOTTableName::Record::PlatformID::Windows:
            if (SkOTTableName::Record::EncodingID::Windows::UnicodeBMPUCS2 != nameRecord.encodingID.windows.value &&
                SkOTTableName::Record::EncodingID::Windows::UnicodeUCS4 != nameRecord.encodingID.windows.value &&
                SkOTTableName::Record::EncodingID::Windows::Symbol != nameRecord.encodingID.windows.value) {
                record.name.reset();
                break; // continue?
            }
            [[fallthrough]];
        case SkOTTableName::Record::PlatformID::Unicode:
        case SkOTTableName::Record::PlatformID::ISO:
            SkString_from_UTF16BE(nameString, nameLength, record.name);
            break;

        case SkOTTableName::Record::PlatformID::Macintosh:
            // TODO: need better decoding, especially on Mac.
            if (SkOTTableName::Record::EncodingID::Macintosh::Roman != nameRecord.encodingID.macintosh.value) {
                record.name.reset();
                break; // continue?
            }
            SkStringFromMacRoman(nameString, nameLength, record.name);
            break;

        case SkOTTableName::Record::PlatformID::Custom:
            // These should never appear in a 'name' table.
        default:
            SkASSERT(false);
            record.name.reset();
            break; // continue?
    }

    // Determine the language.
    const uint16_t languageID = SkEndian_SwapBE16(nameRecord.languageID.languageTagID);

    // Handle format 1 languages.
    if (SkOTTableName::format_1 == nameTable.format && languageID >= 0x8000) {
        const uint16_t languageTagRecordIndex = languageID - 0x8000;

        if (nameRecordsSize < sizeof(nameRecord) * nameRecordsCount) {
            return false; // "und" or break?
        }
        const uint8_t *format1extData = nameRecords + sizeof(nameRecord) * nameRecordsCount;
        size_t format1extSize = nameRecordsSize - sizeof(nameRecord) * nameRecordsCount;
        SkOTTableName::Format1Ext format1ext;
        if (format1extSize < sizeof(format1ext)) {
            return false; // "und" or break?
        }
        memcpy(&format1ext, format1extData, sizeof(format1ext));

        const uint8_t *languageTagRecords = format1extData + sizeof(format1ext);
        size_t languageTagRecordsSize = format1extSize - sizeof(format1ext);
        if (languageTagRecordIndex < SkEndian_SwapBE16(format1ext.langTagCount)) {
            SkOTTableName::Format1Ext::LangTagRecord languageTagRecord;
            if (languageTagRecordsSize < sizeof(languageTagRecord) * (languageTagRecordIndex + 1)) {
                return false; // "und"?
            }
            const uint8_t *languageTagData = languageTagRecords + sizeof(languageTagRecord) * languageTagRecordIndex;
            memcpy(&languageTagRecord, languageTagData, sizeof(languageTagRecord));

            uint16_t languageOffset = SkEndian_SwapBE16(languageTagRecord.offset);
            uint16_t languageLength = SkEndian_SwapBE16(languageTagRecord.length);

            if (fNameTableSize < stringTableOffset + languageOffset + languageLength) {
                return false; // "und"?
            }
            const uint8_t *languageString = stringTable + languageOffset;
            SkString_from_UTF16BE(languageString, languageLength, record.language);
            return true;
        }
    }

    // Handle format 0 languages, translating them into BCP 47.
    const BCP47FromLanguageId target = { languageID, "" };
    int languageIndex = SkTSearch<BCP47FromLanguageId, BCP47FromLanguageIdLess>(BCP47FromLanguageID,
        std::size(BCP47FromLanguageID), target, sizeof(target));
    if (languageIndex >= 0) {
        record.language = BCP47FromLanguageID[languageIndex].bcp47;
        return true;
    }

    // Unknown language, return the BCP 47 code 'und' for 'undetermined'.
    record.language = "und";
    return true;
}
